package file

import (
	"errors"
	"io/ioutil"
	"os"

	library "../.."
)

///
// 文件管理库
// 	 文件库为带缓冲的本地文件提供写与读操作
///

// ProcessLock 流程锁类
// ! 注:
// !	这是一个十分简单的锁处理
// !	在没有锁情况下Lock函数执行为返回,加锁情况下进行锁
// !	加锁SetLock函数执行后必须执行Clear对其解锁,且不能多协程同时加/解锁
type ProcessLock struct {
	Setlock bool // 上锁标志
	count   uint
	lock    chan bool
}

// Lock 锁
func (lock *ProcessLock) Lock() {
	// * 检测锁状态
	if lock.Setlock {
		// *上锁
		lock.count++
		lock.lock <- true
	}
}

// SetLock 上锁
func (lock *ProcessLock) SetLock() {
	lock.Setlock = true
}

// Clear 解锁
func (lock *ProcessLock) Clear() {
	// * 是否上锁
	if lock.Setlock {
		// * 解除锁状态
		lock.Setlock = false
		// * 清除已锁
		for ; lock.count > 0; lock.count-- {
			<-lock.lock
		}
	}
}

// NewProcessLock 创建锁
func NewProcessLock() *ProcessLock {
	return &ProcessLock{
		lock: make(chan bool),
	}
}

// ItemFile 文件类
// ! 注:
// !	库并没有对底层切片做重新利用
// !	超时时间太短或短时间内对大量数据做释放/添加可能会引起Gc调度压力
// !	建议对不同优先度的文件做不同的超时处理
// !		如: 对经常被请求的文件做超时时间适当增长或者设置不超时已确保框架调度器延迟
type ItemFile struct {
	Data []byte // * 文件数据
	// ? 标志信息
	Sync bool  // * 同步标志位
	time int64 // * 过期时间
}

// SetTime 设置过期时间
func (Item *ItemFile) SetTime(Time int64) {
	Item.time = Time
}

// GetTime 得到过期时间
func (Item *ItemFile) GetTime() int64 {
	return Item.time
}

// Cache  文件缓冲库
type Cache struct {
	Path      string               // * 文件路径
	CacheList map[string]*ItemFile // * 缓冲列表 [文件名]目标文件
	Time      int64                // * 过期时间 公用参数 单位秒
	// 内部变量
	lock ProcessLock
}

// Open 打开文件
func (File *Cache) Open(Name string) (*ItemFile, error) {
	File.lock.Lock()
	var (
		f   *os.File
		err error
	)
	// * 验证文件路径
	if !library.IsPath(File.Path, Name) {
		err = errors.New("异常路径访问")
		return nil, err
	}
	// * 打开文件
	f, err = os.OpenFile(File.Path+Name, os.O_RDWR, 0600)
	defer f.Close()
	if err != nil {
		return nil, err
	}
	// * 读入数据
	var item = &ItemFile{time: File.Time}
	item.Data, _ = ioutil.ReadAll(f)
	File.CacheList[Name] = item
	return item, nil
}

// Read 读文件
func (File *Cache) Read(Name string) ([]byte, error) {
	File.lock.Lock()
	// * 查询文件
	if file, ok := File.CacheList[Name]; ok {
		if file.time != -1 {
			// * 重置时间
			file.time = File.Time
		}
		return file.Data, nil
	}
	return nil, errors.New("文件不存在")
}

// Write 写文件
func (File *Cache) Write(Name string, Data []byte) error {
	File.lock.Lock()
	// * 查询文件
	if file, ok := File.CacheList[Name]; ok {
		// * 检测标志位
		file.Data = Data
		file.Sync = true
		if file.time != 0 {
			// * 重置时间
			file.time = File.Time
		}
		return nil
	}
	return errors.New("文件不存在")
}

// GetItemFile 得到文件类
func (File *Cache) GetItemFile(Name string) (*ItemFile, error) {
	File.lock.Lock()
	// * 查询文件
	if file, ok := File.CacheList[Name]; ok {
		return file, nil
	}
	return nil, errors.New("文件不存在")
}

// NewCache 创建文件缓冲
// ? Path string	本地文件储存目录
// ? Time int64		缓存超时时间
func NewCache(Path string, Time int64) (File *Cache, TimeID int64) {
	File = &Cache{
		Path:      Path,
		Time:      Time,
		CacheList: make(map[string]*ItemFile, 0),
	}
	// * 启动调度
	// !	调度是1秒处理一次
	TimeID = library.Cycle.ADD(1000, func() {
		// * 保证唯一性
		if !File.lock.Setlock {
			// * 处理调度
			for Name, file := range File.CacheList {
				if file.time > 0 {
					file.time--
				}
				if file.time == 0 {
					// * 删除
					File.lock.SetLock() // ? 设置锁
					delete(File.CacheList, Name)
					File.lock.Clear() // ? 解锁
					// * 写文件
					if file.Sync {
						// * 储存到文件中
						f, err := os.OpenFile(File.Path+Name, os.O_RDWR|os.O_CREATE, 0600)
						defer f.Close()
						if err != nil {
							return
						}
						f.Truncate(0) // ? 清空文件
						f.Write(file.Data)
					}
				}
			}
		}
	})
	return
}
